Skip to content

Give a script none for an object that no longer exists, instead of crashing - #553

Merged
cybersecurity merged 1 commit into
testingfrom
script-stale-object-index
Nov 5, 2026
Merged

cybersecurity merged 1 commit into
testingfrom
script-stale-object-index

Conversation

@GuvenHuso

Copy link
Copy Markdown
Collaborator

A dedicated server on build-337 crashed during a CTF game on the Custom Edition map coldsnap_20th_xl_rc5, with about 12 players in:

EXCEPTION assert in c:\halo\SOURCE\memory\data.c,#412: object index #449 (0xe43001c1) is unused or changed (release build)
segmentation fault (signal 11, code 1) at 0x8

Symbolising the release binary gives object_get_and_verify_type ← scenario_trigger_volume_test_object ← scenario_trigger_volume_test_object_evaluate ← hs_thread_main. The map's script ran volume_test_object on a Warthog that had already been deleted. A script keeps an object's index after the object is gone, in a global or an object list, and nothing between the script and object_get checked it. In a release build datum_get reports the stale index and returns NULL, and the read through that NULL faults.

The fix

The script runtime now turns such a value into none, which is already what a script gets for a named object that doesn't exist.

  • hs_cast checks every global read and every function's or script's result, before casting, so no list is made of a deleted object.
  • hs_arguments_evaluate checks the arguments again just before the call, because a later argument's static script can sleep after an earlier one was evaluated.
  • Object lists: a list that has been collected counts as gone too, and walking a list passes over deleted objects. list_count still counts every entry, as it always has.
  • unit_kill, unit_kill_silent and scenery_get_animation_time crashed on none, so they now do nothing for it. The other script functions with object parameters were searched for a check of none, and those without one were read.

Nothing the machines send each other changes, so the network version stays. Scripts need not agree between machines: only the host runs them in co-op, and in other games the host decides what they do to the game.

Testing

  • New harness test tools/harness/tests/test_script_stale_objects.py, on the real runtime and data arrays, with negative controls: 42 pass.
  • Locally on Windows, the full harness has the same 631 failures as testing (Linux-only tests) and no new ones. The four changed source files compile for Windows; CI builds the other platforms.
  • Not run on the real map. The crash depends on what happens to that Warthog in play, so it hasn't been reproduced in a game, only in the harness.

🤖 Generated with Claude Code

…ashing

A dedicated server playing CTF on the Custom Edition map
coldsnap_20th_xl_rc5 crashed in hs_thread_main: the map's script ran
volume_test_object on a Warthog that had been deleted. A script keeps an
object's index after the object is gone, in a global or an object list, and
nothing between the script and object_get checked it. In a release build
datum_get reports the stale index and returns NULL, and the read through
that NULL faults. Halo PC plays this map.

The script runtime now turns such a value into none. hs_cast checks every
global read and every function's or script's result, before casting, so no
list is made of a deleted object. hs_arguments_evaluate checks the arguments
again before the call, because a later argument's static script can sleep
after an earlier one was evaluated. Lists collected in the same way count as
gone too. Walking an object list passes over deleted objects; list_count
still counts every entry, as it always has.

A script already gets none for a named object that doesn't exist, and nearly
every script function takes it. The functions with object parameters were
searched for a check of none, and those without one were read: unit_kill,
unit_kill_silent and scenery_get_animation_time crashed on none, so they now
do nothing for it.

HALO_PORT_NETWORK_VERSION stays the same: nothing sent changes, and scripts
need not agree between machines. Only the host runs them in co-op, and in
other games the host decides what they do to the game.

Checked with tools/harness/tests/test_script_stale_objects.py (the real
runtime and data arrays, with negative controls): 42 pass. On Windows the
full harness has the same 631 failures as testing and no new ones. The four
changed files compile for Windows; CI builds the other platforms. Not run on
the real map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@VerySmuggy VerySmuggy added bug Something isn't working priority: high Should be handled soon labels Nov 5, 2026
@cybersecurity
cybersecurity merged commit d315ad0 into testing Nov 5, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: high Should be handled soon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants